<template>
  <Form ref="formElRef" :class="getFormClass" v-bind="getBindValue" :model="formModel">
    <Row v-bind="getRow">
      <template v-for="schema in getSchema" :key="schema.field">
        <FormItem
          :allDefaultValues="defaultValueRef"
          :formActionType="formActionType"
          :formModel="formModel"
          :formProps="getProps"
          :labelCol="{ span: 3, offset: 12 }"
          :schema="schema"
          :setFormModel="setFormModel"
          :tableAction="tableAction"
        >
          <template v-for="item in Object.keys($slots)" #[item]="data">
            <slot :name="item" v-bind="data || {}"></slot>
          </template>
        </FormItem>
      </template>
      <span class="md:float-right">
        <FormAction v-bind="getFormActionBindProps"></FormAction>
      </span>
    </Row>
  </Form>
</template>

<script lang="ts" setup>
import { Form, Row } from 'ant-design-vue';
import FormAction from '/@/components/BasicForm/components/FormActions.vue';
import FormItem from '/@/components/BasicForm/components/BasicFormItem.vue';
import { basicProps } from '/@/components/BasicForm/props';
import { computed, nextTick, onMounted, reactive, Ref, ref, unref, useAttrs, watch } from 'vue';
import { FormActionType, FormProps, FormSchema } from '/@/components/BasicForm/type/form';

import { createFormContext } from '/@/components/BasicForm/hooks/useFormContext';
import { useFormEvents } from '/@/components/BasicForm/hooks/useFormEvents';
import { dateUtil } from '/@/utils/dateUtil';
import { dateItemType } from '/@/components/BasicForm/helper';
import { useFormValues } from '/@/components/BasicForm/hooks/useFormValues';
import { deepMerge } from '/@/utils';
import moment from 'moment';
import { useContext } from '/@/hooks/core/useContext';
import { useDesign } from '/@/hooks/web/useDesign';

const props = defineProps(basicProps);
const emits = defineEmits(['advanced-change', 'reset', 'submit', 'register']);
const attrs = useAttrs();
const modalFn = useContext<{ redoModalHeight: () => void }>(Symbol(''));
const { prefixCls } = useDesign('basic-form');
const formElRef = ref<Nullable<FormActionType>>(null);
const schemaRef = ref<Nullable<FormSchema[]>>(null);
const isInitedDefaultRef = ref(false);
const formModel = reactive<Recordable>({});
const defaultValueRef = ref<Recordable>({});
const propsRef = ref<Partial<FormProps>>({});
const getProps = computed((): FormProps => {
  return { ...props, ...unref(propsRef), ...formModel } as FormProps;
});
const getBindValue = computed(() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable));

const getFormActionBindProps = computed((): Recordable => ({ ...getProps.value }));

const getSchema = computed((): FormSchema[] => {
  const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
  for (const schema of schemas) {
    const { defaultValue, component } = schema;
    // handle date type
    if (defaultValue && dateItemType.includes(component)) {
      if (!Array.isArray(defaultValue)) {
        schema.defaultValue = dateUtil(defaultValue);
      } else {
        const def: moment.Moment[] = [];
        defaultValue.forEach((item) => {
          def.push(dateUtil(item));
        });
        schema.defaultValue = def;
      }
    }
  }
  return schemas as FormSchema[];
});

const getRow = computed((): Recordable => {
  const { baseRowStyle = {}, rowProps } = unref(getProps);
  return {
    style: baseRowStyle,
    ...rowProps,
  };
});

const getFormClass = computed(() => {
  return [
    prefixCls,
    {
      [`${prefixCls}--compact`]: unref(getProps).compact,
    },
  ];
});

const { handleFormValues, initDefault } = useFormValues({
  getProps,
  defaultValueRef,
  getSchema,
  formModel,
});
const {
  handleSubmit,
  setFieldsValue,
  clearValidate,
  validate,
  validateFields,
  getFieldsValue,
  updateSchema,
  resetSchema,
  appendSchemaByField,
  removeSchemaByFiled,
  resetFields,
  scrollToField,
} = useFormEvents({
  emits,
  getProps,
  formModel,
  getSchema,
  defaultValueRef,
  formElRef: formElRef as Ref<FormActionType>,
  schemaRef: schemaRef as Ref<FormSchema[]>,
  handleFormValues,
});

createFormContext({
  resetAction: resetFields,
  submitAction: handleSubmit,
});

watch(
  () => unref(getProps).model,
  () => {
    const { model } = unref(getProps);
    if (!model) return;
    setFieldsValue(model);
  },
  {
    immediate: true,
  }
);

watch(
  () => unref(getProps).schemas,
  (schemas) => {
    resetSchema(schemas ?? []);
  }
);

watch(
  () => getSchema.value,
  (schema) => {
    nextTick(() => {
      //  Solve the problem of modal adaptive height calculation when the form is placed in the modal
      modalFn?.redoModalHeight?.();
    });
    if (unref(isInitedDefaultRef)) {
      return;
    }
    if (schema?.length) {
      initDefault();
      isInitedDefaultRef.value = true;
    }
  }
);

async function setProps(formProps: Partial<FormProps>): Promise<void> {
  propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
}

function setFormModel(key: string, value: any) {
  formModel[key] = value;
  const { validateTrigger } = unref(getBindValue);
  if (!validateTrigger || validateTrigger === 'change') {
    validateFields([key]).catch(() => {
      return;
    });
  }
}

const formActionType: Partial<FormActionType> = {
  getFieldsValue,
  setFieldsValue,
  resetFields,
  updateSchema,
  resetSchema,
  setProps,
  removeSchemaByFiled,
  appendSchemaByField,
  clearValidate,
  validateFields,
  validate,
  submit: handleSubmit,
  scrollToField: scrollToField,
};

onMounted(() => {
  initDefault();
  emits('register', formActionType);
});
</script>

<style lang="less">
@prefix-cls: ~'@{namespace}-basic-form';

.@{prefix-cls} {
  .ant-form-item {
    &-label label::after {
      margin: 0 6px 0 2px;
    }

    &-with-help {
      margin-bottom: 0;
    }

    &:not(.ant-form-item-with-help) {
      margin-bottom: 20px;
    }

    &.suffix-item {
      .ant-form-item-children {
        display: flex;
      }

      .ant-form-item-control {
        margin-top: 4px;
      }

      .suffix {
        display: inline-flex;
        padding-left: 6px;
        margin-top: 1px;
        line-height: 1;
        align-items: center;
      }
    }
  }

  .ant-form-explain {
    font-size: 14px;
  }

  &--compact {
    .ant-form-item {
      margin-bottom: 8px !important;
    }
  }
}
</style>
